package ru.qatools.clay.aether;
import org.apache.commons.io.FileUtils;
import org.apache.maven.model.DistributionManagement;
import org.apache.maven.settings.Settings;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.deployment.DeployRequest;
import org.eclipse.aether.deployment.DeploymentException;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.installation.InstallRequest;
import org.eclipse.aether.installation.InstallationException;
import org.eclipse.aether.repository.RemoteRepository;
import org.eclipse.aether.resolution.ArtifactRequest;
import org.eclipse.aether.resolution.ArtifactResolutionException;
import org.eclipse.aether.resolution.DependencyRequest;
import org.eclipse.aether.resolution.DependencyResolutionException;
import org.eclipse.aether.util.artifact.JavaScopes;
import org.eclipse.aether.util.artifact.SubArtifact;
import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator;
import ru.qatools.clay.maven.settings.FluentModelBuilder;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static ru.qatools.clay.aether.internal.AetherUtils.*;
/**
* @author Ilya Sadykov smecsia@yandex-team.ru
* @author Innokenty Shuvalov innokenty@yandex-team.ru
* @author Dmitry Baev charlie@yandex-team.ru
*/
public class Aether {
public static final String MAVEN_CENTRAL_URL = "http://repo1.maven.org/maven2";
public static final String POM_EXTENSION = "pom";
public static final String MODEL_VERSION = "4.0.0";
public static final String JAR = "jar";
public static final String POM_XML = "pom.xml";
public static final String AETHER_TEMP_FILE_PREFIX = "clay-aether";
private RepositorySystemSession session;
private RepositorySystem system;
private List<RemoteRepository> repositories;
private String scope = JavaScopes.RUNTIME;
Aether(File localRepoDir, Settings settings) {
this.system = newRepositorySystem();
this.session = newSession(system, settings, localRepoDir);
this.repositories = getRepositoriesAsList(session, settings);
}
Aether(RepositorySystem system, RepositorySystemSession session, List<RemoteRepository> repositories) {
this.session = session;
this.system = system;
this.repositories = repositories;
}
public static Aether aether(File localRepoDir) {
return aether(localRepoDir, null);
}
public static Aether aether(File localRepoDir, Settings settings) {
return new Aether(localRepoDir, settings);
}
public static Aether aether(Settings settings) {
return new Aether(new File(settings.getLocalRepository()), settings);
}
public static Aether aether(RepositorySystem system, RepositorySystemSession session, List<RemoteRepository> repositories) {
return new Aether(system, session, repositories);
}
/**
* Using this method you can specify resolve scope, RUNTIME by default
*
* @param scope specified scope to resolving artifacts
*/
public Aether scope(String scope) {
this.scope = scope;
return this;
}
/**
* Other way to run {@link #resolve(org.eclipse.aether.artifact.Artifact)}
*
* @param artifactCoordinates The artifact coordinates in the format
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
*/
public AetherResult resolve(String artifactCoordinates) throws AetherException {
return resolve(new DefaultArtifact(artifactCoordinates));
}
/**
* Shortcut for {@link #resolve(org.eclipse.aether.artifact.Artifact, boolean)}
*/
public AetherResult resolve(Artifact artifact) throws AetherException {
return resolve(artifact, true);
}
/**
* Other way to run {@link #resolve(org.eclipse.aether.artifact.Artifact, boolean)}
*
* @param artifactCoordinates The artifact coordinates in the format
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
*/
public AetherResult resolve(String artifactCoordinates, boolean withTransitives) throws AetherException {
return resolve(new DefaultArtifact(artifactCoordinates), withTransitives);
}
/**
* Resolve given artifact with or without transitives
* {@link #resolveWithTransitives(org.eclipse.aether.artifact.Artifact)}
* {@link #resolveWithoutTransitives(org.eclipse.aether.artifact.Artifact...)}
*
* @param artifact the given artifacts to resolve
* @param withTransitives true if you need to resolve artifact with transitives, false otherwise
*/
public AetherResult resolve(Artifact artifact, boolean withTransitives) throws AetherException {
return withTransitives ? resolveWithTransitives(artifact) : resolveWithoutTransitives(artifact);
}
/**
* Other way to run {@link #resolveAll(org.eclipse.aether.artifact.Artifact...)}
*
* @param artifactsCoordinates The array of artifact coordinates in the format
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
*/
public AetherResult resolveAll(String... artifactsCoordinates) throws AetherException {
List<Artifact> artifacts = new ArrayList<>();
for (String coordinate : artifactsCoordinates) {
artifacts.add(new DefaultArtifact(coordinate));
}
return resolveAll(artifacts.toArray(new Artifact[artifacts.size()]));
}
/**
* Resolve all given artifacts {@link #resolveWithoutTransitives(org.eclipse.aether.artifact.Artifact...)}
* without transitives
*
* @param artifacts the given artifacts to resolve
* @return The resolution result, never {@code null}.
* @throws AetherException if can't resolve given artifacts
*/
public AetherResult resolveAll(Artifact... artifacts) throws AetherException {
return resolveWithoutTransitives(artifacts);
}
/**
* Collects and resolves the transitive dependencies of an artifact. This operation is essentially a combination of
* {@link org.eclipse.aether.RepositorySystem#collectDependencies(RepositorySystemSession, CollectRequest)} and
* {@link org.eclipse.aether.RepositorySystem#resolveArtifacts(RepositorySystemSession, java.util.Collection)}.
*
* @param artifact The artifact to resolve, may be {@code null}.
* @return The resolution result {@link AetherResult}, never {@code null}.
* @throws AetherException if can't resolve given artifact
*/
protected AetherResult resolveWithTransitives(Artifact artifact) throws AetherException {
try {
CollectRequest collectRequest = new CollectRequest(
new Dependency(artifact, scope),
repositories()
);
DependencyRequest request = new DependencyRequest(collectRequest, null);
return new AetherResult(system.resolveDependencies(session, request).getArtifactResults());
} catch (DependencyResolutionException e) {
throw new AetherException("Can't resolve given artifact " + artifact, e);
}
}
/**
* Resolves the paths for a collection of artifacts. Artifacts will be downloaded to the local repository if
* necessary. Artifacts that are already resolved will be skipped and are not re-resolved. In general, callers must
* not assume any relationship between an artifact's filename and its coordinates. Note that this method assumes
* that any relocations have already been processed.
*
* @param artifacts Collection of artifacts to resolve, never {@code null}.
* @return The resolution result {@link AetherResult}, never {@code null}.
* @throws AetherException if can't resolve given artifact
*/
protected AetherResult resolveWithoutTransitives(Artifact... artifacts) throws AetherException {
try {
return new AetherResult(system.resolveArtifacts(session, buildArtifactRequests(artifacts)));
} catch (ArtifactResolutionException e) {
throw new AetherException("Can't resolve one or more given artifacts " + Arrays.toString(artifacts), e);
}
}
/**
* Build {@link org.eclipse.aether.resolution.ArtifactRequest} for each given
* {@link org.eclipse.aether.artifact.Artifact}
*
* @param artifacts the given artifacts
* @return list of artifact requests
*/
protected List<ArtifactRequest> buildArtifactRequests(Artifact... artifacts) {
List<ArtifactRequest> requests = new ArrayList<>();
for (Artifact artifact : artifacts) {
requests.add(new ArtifactRequest(artifact, repositories(), null));
}
return requests;
}
/**
* Other way to run {@link #collect(org.eclipse.aether.artifact.Artifact)}
*
* @param artifactCoordinates The artifact coordinates in the format
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
*/
public List<Artifact> collect(String artifactCoordinates) throws AetherException {
return collect(new DefaultArtifact(artifactCoordinates));
}
/**
* Collects the transitive dependencies of an artifact and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies. To also resolve the actual
* artifact files, use {@link #resolve(org.eclipse.aether.artifact.Artifact)}.
*
* @param artifact the given artifacts to collect
* @throws AetherException if can't collect transitive dependencies for given artifact
*/
public List<Artifact> collect(Artifact artifact) throws AetherException {
try {
if (session.isOffline()) {
throw new AetherException("Can't collect dependencies in offline mode");
}
CollectRequest collectRequest = new CollectRequest(
new Dependency(artifact, scope),
repositories()
);
CollectResult result = system.collectDependencies(session, collectRequest);
PreorderNodeListGenerator visitor = new PreorderNodeListGenerator();
result.getRoot().accept(visitor);
return visitor.getArtifacts(true);
} catch (DependencyCollectionException e) {
throw new AetherException("Can't collect given artifact " + artifact, e);
}
}
/**
* Shortcut for {@link #install(java.io.File, String, String, String, String)}
*/
public void install(File jar, String groupId, String artifactId, String version) throws AetherException {
install(jar, groupId, artifactId, JAR, version);
}
/**
* Shortcut for {@link #install(java.io.File, String, String, String, String, String)}
*/
public void install(File jar, String groupId, String artifactId, String extension, String version)
throws AetherException {
install(jar, groupId, artifactId, "", extension, version);
}
/**
* Shortcut for {@link #install(java.io.File, java.io.File, String, String, String, String, String)}
*/
public void install(File jar, String groupId, String artifactId, String classifier, String extension, String version)
throws AetherException {
File pom = null;
try {
pom = createPomFile(groupId, artifactId, version);
install(jar, pom, groupId, artifactId, classifier, extension, version);
} finally {
FileUtils.deleteQuietly(pom);
}
}
/**
* Install given jars to local repository using specified artifact coordinates.
*
* @param jar archive with given artifact to install
* @param pom archive with pom.xml
* @throws AetherException If any artifact/metadata from the request could not be installed.
*/
public void install(File jar, File pom, String groupId, String artifactId, String classifier, String extension, String version)
throws AetherException {
Artifact jarArtifact = new DefaultArtifact(groupId, artifactId, classifier, extension, version, null, jar);
Artifact pomArtifact = new SubArtifact(jarArtifact, null, POM_EXTENSION, pom);
install(jarArtifact, pomArtifact);
}
/**
* Installs a collection of artifacts and their accompanying metadata to the local repository.
*
* @param artifacts the given artifacts to install
* @throws AetherException If any artifact/metadata from the request could not be installed.
*/
public void install(Artifact... artifacts) throws AetherException {
try {
InstallRequest installRequest = new InstallRequest();
for (Artifact artifact : artifacts) {
installRequest.addArtifact(artifact);
}
system.install(session, installRequest);
} catch (InstallationException e) {
throw new AetherException("Can't install one or more given artifacts " + Arrays.toString(artifacts), e);
}
}
/**
* Shortcut for {@link #deploy(DistributionManagement distribution, java.io.File, String, String, String, String)}
*/
public void deploy(DistributionManagement distribution, File jar, String groupId, String artifactId, String version)
throws AetherException {
deploy(distribution, jar, groupId, artifactId, JAR, version);
}
/**
* Shortcut for {@link #deploy(DistributionManagement distribution, java.io.File, String, String, String, String, String)}
*/
public void deploy(DistributionManagement distribution, File jar, String groupId, String artifactId,
String extension, String version)
throws AetherException {
deploy(distribution, jar, groupId, artifactId, "", extension, version);
}
/**
* Other way to run {@link #deploy(org.apache.maven.model.DistributionManagement, java.io.File, java.io.File, String, String, String, String, String)}
*/
public void deploy(DistributionManagement distribution, File jar, String groupId, String artifactId,
String classifier, String extension, String version) throws AetherException {
File pom = null;
try {
pom = createPomFile(groupId, artifactId, version);
deploy(distribution, jar, pom, groupId, artifactId, classifier, extension, version);
} finally {
FileUtils.deleteQuietly(pom);
}
}
/**
* Other way to run {@link #deploy(org.apache.maven.model.DistributionManagement, org.eclipse.aether.artifact.Artifact...)}
*/
public void deploy(DistributionManagement distribution, File jar, File pom, String groupId, String artifactId,
String classifier, String extension, String version) throws AetherException {
Artifact jarArtifact = new DefaultArtifact(groupId, artifactId, classifier, extension, version, null, jar);
Artifact pomArtifact = new SubArtifact(jarArtifact, null, POM_EXTENSION, pom);
deploy(distribution, jarArtifact, pomArtifact);
}
/**
* Uploads a collection of artifacts and their accompanying metadata to a remote repository.
*
* @param distribution {@link org.apache.maven.model.DistributionManagement} which contains information
* about remotes
* @param artifacts the given artifacts to deploy
* @throws AetherException If any artifact/metadata from the request could not be deployd.
*/
public void deploy(DistributionManagement distribution, Artifact... artifacts) throws AetherException {
try {
if (artifacts.length == 0) {
return;
}
DeployRequest deployRequest = new DeployRequest();
for (Artifact artifact : artifacts) {
deployRequest.addArtifact(artifact);
}
deployRequest.setRepository(toRemoteRepository(
artifacts[0].isSnapshot() ?
distribution.getSnapshotRepository() :
distribution.getRepository(),
session
));
system.deploy(session, deployRequest);
} catch (DeploymentException e) {
throw new AetherException("Can't deploy one or more given artifacts" + Arrays.toString(artifacts), e);
}
}
/**
* Create temp pom.xml file with specified artifact coordinates.
*
* @param groupId group id of artifact for pom.xml
* @param artifactId artifact id of artifact for pom.xml
* @param version version id of artifact for pom.xml
* @return {@link java.io.File} with created pom.xml
* @throws AetherException if can't create temp file {@link java.io.File#createTempFile(String, String)}
* if can't marshall pom.xml to created temp file
*/
protected File createPomFile(String groupId, String artifactId, String version) throws AetherException {
try {
File pom = File.createTempFile(AETHER_TEMP_FILE_PREFIX, POM_XML);
FluentModelBuilder.newPom()
.withGroupId(groupId)
.withArtifactId(artifactId)
.withVersion(version)
.withModelVersion(MODEL_VERSION)
.marshalTo(pom);
return pom;
} catch (IOException e) {
throw new AetherException("Can't create temp file for pom.xml", e);
}
}
/**
* Get remote repositories if online mode, null otherwise
*
* @return list of remotes or null
*/
protected List<RemoteRepository> repositories() {
return session.isOffline() ? null : repositories;
}
}